package com.ruiysoft.pub.lock;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
public class RedisLock {
	
	@Autowired
	@Qualifier("redisTemplate")
	public RedisTemplate<String,String> redisTemplate;
	
	protected final Logger logger = LoggerFactory.getLogger(getClass());
	/**
     * 加锁
     * @param locaName  锁的key
     * @param acquireTimeout  获取超时时间(毫秒)
     * @param timeout   锁的超时时间(毫秒)
     * @return 锁标识
     */
    public String lockWithTimeout(String locaName,
                                  long acquireTimeout, long timeout) {
        String retIdentifier = null;
        try {
            // 随机生成一个value
            String identifier = UUID.randomUUID().toString();
            // 锁名，即key值
            String lockKey = "lock:" + locaName;

            // 获取锁的超时时间，超过这个时间则放弃获取锁
            long nowTime = System.currentTimeMillis();
            long end = nowTime + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (redisTemplate.opsForValue().setIfAbsent(lockKey, identifier)) {
                	redisTemplate.expire(lockKey, timeout, TimeUnit.MILLISECONDS);
                    // 返回value值，用于释放锁时间确认
                    retIdentifier = identifier;
                    logger.info("rlockWithTimeout:identifier"+retIdentifier+"---"+locaName);
                    return retIdentifier;
                }
                // 返回-1代表key没有设置超时时间，为key设置一个超时时间
                if (redisTemplate.getExpire(lockKey) == -1) {
                	redisTemplate.expire(lockKey, timeout, TimeUnit.MILLISECONDS);
                }

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (Exception e) {
        	logger.error("获取锁失败--参数{}--{}--{}", "locaName:"+locaName,"acquireTimeout"+acquireTimeout,"timeout"+timeout,e);
        }
        return retIdentifier;
    }

    /**
     * 释放锁
     * @param lockName 锁的key
     * @param identifier    释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        logger.info("releaseLock:identifier"+identifier+"---"+lockName);
        try {
            while (true) {
                // 监视lock，准备开始事务
            	redisTemplate.watch(lockKey);
                // 通过前面返回的value值判断是不是该锁，若是该锁，则删除，释放锁
                if (StringUtils.isNotBlank(identifier)&&identifier.equals(redisTemplate.opsForValue().get(lockKey))) {
                	Object results =  redisTemplate.execute(new SessionCallback<Object>() {

						@SuppressWarnings({ "unchecked", "rawtypes" })
						@Override
						public Object execute(RedisOperations operations) throws DataAccessException {
							operations.multi();
							operations.delete(lockKey);
							return operations.exec();
						}
					});
                    if (results == null) {
                        continue;
                    }
                    retFlag = true;
                }
                redisTemplate.unwatch();
                break;
            }
        } catch (Exception e) {
        	logger.error("锁释放失败--参数{}--{}", "lockKey:"+lockKey,"identifier"+identifier,e);
        }
        return retFlag;
    }

}
